#include "StdAfx.h"
#include "Node.h"

using namespace Common;
using namespace RenderSystem;

/*
class DebugRenderable : public Renderable
{
public:
DebugRenderable(Node* parent);
virtual ~DebugRenderable();
const Material& getMaterial(void) const;
void getRenderOperation(RenderOperation& op);
virtual void getWorldTransforms(Matrix4* xform) const;
Real getSquaredViewDepth(const Camera* cam) const;
const LightList& getLights(void) const;
void setScaling(Real s) { mScaling = s; }

protected:
Node* mParent;
MeshPtr mMeshPtr;
MaterialPtr mMat;
Real mScaling;
};
*/


Node::Node(void)
{
}


Node::~Node(void)
{
}

void Node::NeedUpdate(void)
{
	m_bUpdateParent = true;
	m_bUpdateChildren = true;
}

const Common::Vector3& Node::GetWorldTranslation( void )
{
	if( m_bUpdateParent )
		this->UpdateFromParent();

	return m_vWorldPosition;
}

const Common::Quaternion& Node::GetWorldOrientation( void )
{
	if( m_bUpdateParent )
		this->UpdateFromParent();

	return m_qWorldOrientation;
}

const Common::Vector3& Node::GetWorldScale( void )
{
	if( m_bUpdateParent )
		this->UpdateFromParent();

	return m_vWorldScale;
}

const Common::Matrix4& Node::GetWorldTransformation( void )
{
	if( m_bUpdateTransformationMatrix )
	{
		m_matWorldTransformation.makeTransform(
			this->GetWorldTranslation(),
			this->GetWorldScale(),
			this->GetWorldOrientation() );

		m_bUpdateTransformationMatrix = false;
	}

	return m_matWorldTransformation;
}

void Node::UpdateFromParent(void)
{
	if( m_pParent != NULL )
	{
		// Update orientation
		const Quaternion& parentOrientation = m_pParent->GetWorldOrientation();
		m_qWorldOrientation = m_bInheritRotation ? parentOrientation * m_qLocalOrientation : m_qLocalOrientation;

		// Update scale
		const Vector3& parentScale = m_pParent->GetWorldScale();
		m_vWorldScale = m_bInheritScale ? parentScale * m_vLocalScale : m_vLocalScale;

		// Change position vector based on parent's orientation & scale
		m_vWorldPosition = parentOrientation * ( parentScale * m_vLocalPosition );

		// Add altered position vector to parents
		m_vWorldPosition += m_pParent->GetWorldTranslation();
	}
	else
	{
		// Root node, no parent
		m_qWorldOrientation = m_qLocalOrientation;
		m_vWorldPosition	= m_vLocalPosition;
		m_vWorldScale		= m_vLocalScale;
	}

	m_bUpdateTransformationMatrix = true;
	m_bUpdateParent = false;
}

Node* Node::CreateChild( const Common::Vector3& translate , const Common::Quaternion& rotate )
{
	Node* pChild = createChildImpl();
	pChild->Translate( translate );
	pChild->Rotate( rotate );

	this->AddChild( pChild );
}

void Node::AddChild(Node* child)
{
	if( child == NULL )
		return;

	if( std::find( m_Children.begin(), m_Children.end(), child ) != m_Children.end() )
		return;

	child->m_pParent = this;
	m_Children.push_back( child );
}

uint32 Node::GetChildrenCount(void) const
{
	return m_Children.size();
}

Node* Node::GetChild( uint32 index ) const
{
	if( index >= m_Children.size() )
		return NULL;

	return m_Children[index];
}

Node* Node::RemoveChild( uint32 index )
{
	Node* pChild =this->GetChild( index );
	if( pChild != NULL )
		this->RemoveChild( pChild );

	return pChild;
}

Node* Node::RemoveChild( Node* pChild )
{
	IteratorChildNode pos = std::find( m_Children.begin(), m_Children.end(), pChild );
	if( pos == m_Children.end() )
		return NULL;

	m_Children.erase( pos );

	pChild->SetParent( NULL );

	return pChild;
}

void Node::SetParent( Node* pChild )
{
	if( pChild == m_pParent )
		return;

	m_pParent = pChild;

	this->NeedUpdate();
}

Node* Node::GetParent(void) const
{
	return m_pParent;
}

void Node::RemoveAllChildren(void)
{
	std::for_each( m_Children.begin(), m_Children.end(), std::bind2nd(std::mem_fun1( Node::SetParent), NULL ) );
	m_Children.clear();
}

void Node::SetInheritance( bool* bInheritTranslation, bool* bInheritRotation, bool* bInheritScale )
{
	bool bInheritanceChanged = false;

	if( bInheritTranslation != NULL )
	{
		m_bInheritTranslation = *bInheritTranslation;
		bInheritanceChanged = true;
	}

	if( bInheritRotation != NULL )
	{
		m_bInheritRotation = *bInheritRotation;
		bInheritanceChanged = true;
	}

	if( bInheritScale != NULL )
	{
		m_bInheritScale = *bInheritScale;
		bInheritanceChanged = true;
	}

	if( bInheritanceChanged )
		this->NeedUpdate();
}

void Node::GetInheritance( bool* bInheritTranslation, bool* bInheritRotation, bool* bInheritScale )
{
	if( bInheritTranslation != NULL )
		*bInheritTranslation = m_bInheritTranslation;

	if( bInheritRotation != NULL )
		*bInheritRotation = m_bInheritRotation;

	if( bInheritScale != NULL )
		*bInheritScale = m_bInheritScale;
}

Matrix3 Node::GetLocalAxes(void) const
{
	Matrix3 matLocalAxes( Matrix3::IDENTITY );
	Matrix3 matRotation;
	m_qWorldOrientation.ToRotationMatrix(matRotation);

	return matRotation * matLocalAxes;
}

Common::Vector3 Node::ConvertWorldToLocalPosition( const Common::Vector3& worldPos )
{
	//return this->GetWorldTranslation().Inverse() * (worldPos - mDerivedPosition) / mDerivedScale;
	const Matrix4& matWorldTransformation = this->GetWorldTransformation();
	return matWorldTransformation.inverse() * worldPos;
}

Common::Vector3 Node::ConvertLocalToWorldPosition( const Common::Vector3& localPos )
{
	const Matrix4& matWorldTransformation = this->GetWorldTransformation();
	return matWorldTransformation * localPos;
}

Common::Quaternion Node::ConvertWorldToLocalOrientation( const Common::Quaternion& worldOrientation )
{
	const Quaternion& rRotationWS = this->GetWorldOrientation();
	return rRotationWS.Inverse() * worldOrientation;
}

Common::Quaternion Node::ConvertLocalToWorldOrientation( const Common::Quaternion& localOrientation )
{
	const Quaternion& rRotationWS = this->GetWorldOrientation();
	return rRotationWS * localOrientation;
}

void Node::_Update(bool updateChildren, bool bParentHasUpdated)
{
	// Short circuit the off case
	if (!updateChildren && !m_bUpdateParent && !m_bUpdateChildren && !bParentHasUpdated )
		return;

	if ( m_bUpdateParent && bParentHasUpdated )
		UpdateFromParent();

	if ( m_bUpdateChildren && updateChildren )
	{
		IteratorChildNode itrChild = m_Children.begin();
		for( ; itrChild != m_Children.end(); ++itrChild )
			(*itrChild)->_Update( true, true );
	}

	m_bUpdateChildren = false;
}